home *** CD-ROM | disk | FTP | other *** search
/ Experimental BBS Explossion 3 / Experimental BBS Explossion III.iso / c / tde31.zip / FILE.C < prev    next >
C/C++ Source or Header  |  1993-08-29  |  68KB  |  2,044 lines

  1. /*
  2.  * This file contains the file i/o stuff.  These functions get stuff from
  3.  * from the outside world into a form for TDE.  The form TDE uses is a
  4.  * double linked list.  Each node in the list points to the prev and
  5.  * the next nodes, if they exist.  Also in each node is a pointer to a
  6.  * line of text, a line length variable, and a dirty node indicator.  In
  7.  * earlier versions of TDE, a '\n' was used to terminate a line of text.
  8.  * In this version, we must keep an accurate count of characters in
  9.  * each line of text, as no character is used to terminate the line.
  10.  *
  11.  * Each file must contain at least one node.  That node is called the
  12.  * EOF node.  The EOF node terminates the double linked list for each
  13.  * file.  The EOF node has a NULL pointer in the line field, a NULL
  14.  * pointer in the next field, and an EOF in the len field.  Here's
  15.  * a crude picture of the double linked list structure:
  16.  *
  17.  *              Regular node                             EOF node
  18.  *     ---------                                 ---------
  19.  *     | prev  | ---> pointer to prev node       | prev  | ---> unknown
  20.  *     | line  | ---> "Hello world"              | line  | ---> NULL
  21.  *     | len   | ---> 11                         | len   | ---> EOF
  22.  *     | dirty | ---> TRUE | FALSE               | dirty | ---> FALSE
  23.  *     | next  | ---> pointer to next node       | next  | ---> NULL
  24.  *     ---------                                 ---------
  25.  *
  26.  * Implicitly, I am assuming that EOF is defined as (-1) in stdio.h.
  27.  *
  28.  * The load_file function is probably more complicated than expected, but
  29.  * I was trying to read chunks of text that match the disk cluster size
  30.  * and/or some multiple of the cache in the disk controller.
  31.  *
  32.  *
  33.  * New editor name:  TDE, the Thomson-Davis Editor.
  34.  * Author:           Frank Davis
  35.  * Date:             June 5, 1991, version 1.0
  36.  * Date:             July 29, 1991, version 1.1
  37.  * Date:             October 5, 1991, version 1.2
  38.  * Date:             January 20, 1992, version 1.3
  39.  * Date:             February 17, 1992, version 1.4
  40.  * Date:             April 1, 1992, version 1.5
  41.  * Date:             June 5, 1992, version 2.0
  42.  * Date:             October 31, 1992, version 2.1
  43.  * Date:             April 1, 1993, version 2.2
  44.  * Date:             June 5, 1993, version 3.0
  45.  * Date:             August 29, 1993, version 3.1
  46.  *
  47.  * This code is released into the public domain, Frank Davis.
  48.  * You may distribute it freely.
  49.  */
  50.  
  51.  
  52. #include "tdestr.h"             /* tde types */
  53. #include "common.h"
  54. #include "define.h"
  55. #include "tdefunc.h"
  56.  
  57.  
  58. #include <dos.h>                /* for renaming files */
  59. #include <bios.h>               /* for direct BIOS keyboard input */
  60. #include <io.h>                 /* for file attribute code */
  61. #include <fcntl.h>              /* open flags */
  62. #if defined( __MSC__ )
  63.    #include <errno.h>
  64.    #include <sys\types.h>       /* S_IWRITE etc */
  65. #endif
  66. #include <sys\stat.h>           /* S_IWRITE etc */
  67.  
  68.  
  69. /*
  70.  * Name:    hw_fattrib
  71.  * Purpose: To determine the current file attributes.
  72.  * Date:    December 26, 1991
  73.  * Passed:  name: name of file to be checked
  74.  * Returns: use the function in the tdeasm file to get the DOS file
  75.  *          attributes.  get_fattr() returns 0 or OK if no error.
  76.  */
  77. int  hw_fattrib( char *name )
  78. {
  79. register int rc;
  80. int  fattr;
  81.  
  82.    rc = get_fattr( name, &fattr );
  83.    return( rc == OK ? rc : ERROR );
  84. }
  85.  
  86.  
  87. /*
  88.  * Name:    change_mode
  89.  * Purpose: To prompt for file access mode.
  90.  * Date:    January 11, 1992
  91.  * Passed:  name:  name of file
  92.  *          line:  line to display message
  93.  * Returns: OK if file could be changed
  94.  *          ERROR otherwise
  95.  * Notes:   function is used to change file attributes for save_as function.
  96.  */
  97. int  change_mode( char *name, int line )
  98. {
  99. int  result;
  100. int  fattr;
  101. register int rc;
  102. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  103.  
  104.    rc = OK;
  105.    result = get_fattr( name, &fattr );
  106.    if (result != OK)
  107.       rc = ERROR;
  108.    else if (result == OK && fattr & READ_ONLY) {
  109.       /*
  110.        * file is read only
  111.        */
  112.       save_screen_line( 0, line, line_buff );
  113.       /*
  114.        * file is write protected. overwrite anyway (y/n)?
  115.        */
  116.       set_prompt( main6, line );
  117.       if (get_yn( ) != A_YES)
  118.          rc = ERROR;
  119.       if (rc == OK && set_fattr( name, ARCHIVE ) != OK)
  120.          rc = ERROR;
  121.       restore_screen_line( 0, line, line_buff );
  122.    }
  123.    return( rc );
  124. }
  125.  
  126.  
  127. /*
  128.  * Name:    write_file
  129.  * Purpose: To write text to a file
  130.  *           way.
  131.  * Date:    June 5, 1991
  132.  * Passed:  name:  name of disk file or device
  133.  *          open_mode:  fopen flags to be used in open
  134.  *          file:  pointer to file structure to write
  135.  *          start: first node to write
  136.  *          end:   last node to write
  137.  *          block: write a file or a marked block
  138.  * Returns: OK, or ERROR if anything went wrong
  139.  */
  140. int  write_file( char *name, int open_mode, file_infos *file, long start,
  141.                  long end, int block )
  142. {
  143. FILE *fp;       /* file to be written */
  144. char *p;
  145. char *z = "\x1a";
  146. register int rc;
  147. int  bc;
  148. int  ec;
  149. int  len;
  150. int  write_z;
  151. int  write_eol;
  152. long number;
  153. line_list_ptr ll;
  154. char *open_string;
  155. char *eol;
  156. size_t eol_count;
  157.  
  158.    write_z = mode.control_z;
  159.    switch (open_mode) {
  160.       case APPEND :
  161.          open_string = "ab";
  162.          break;
  163.       case OVERWRITE :
  164.       default :
  165.          open_string = "wb";
  166.          break;
  167.    }
  168.    switch (file->crlf) {
  169.       case BINARY   :
  170.          eol_count = 0;
  171.          eol = "";
  172.          write_z = FALSE;
  173.          break;
  174.       case CRLF   :
  175.          eol_count = 2;
  176.          eol = "\r\n";
  177.          break;
  178.       case LF     :
  179.          eol_count = 1;
  180.          eol = "\n";
  181.          break;
  182.       default     :
  183.          assert( FALSE );
  184.    }
  185.    rc = OK;
  186.    if ((fp = fopen( name, open_string )) == NULL || ceh.flag == ERROR)
  187.       rc = ERROR;
  188.    else {
  189.       ec = bc = len = 0;
  190.       ll = file->line_list;
  191.       if (block == LINE || block == BOX || block == STREAM) {
  192.          if (g_status.marked_file == NULL)
  193.             rc = ERROR;
  194.          else
  195.             file = g_status.marked_file;
  196.          if (rc != ERROR) {
  197.             ll = file->line_list;
  198.             for (number=1; number<start && ll->next != NULL; number++)
  199.                ll = ll->next;
  200.          }
  201.          if (rc != ERROR && (block == BOX || block == STREAM)) {
  202.             bc  = file->block_bc;
  203.             ec  = file->block_ec;
  204.             len = ec + 1 - bc;
  205.          }
  206.          if (rc != ERROR  &&  block == STREAM) {
  207.             if (start == end )
  208.                block = BOX;
  209.          }
  210.       } else {
  211.          for (number=1; number<start && ll->next != NULL; number++)
  212.             ll = ll->next;
  213.       }
  214.       p = g_status.line_buff;
  215.       if (rc == OK) {
  216.          if (block == BOX) {
  217.  
  218.             assert( len >= 0 );
  219.             assert( len < MAX_LINE_LENGTH );
  220.  
  221.             for (;start <= end  &&  ll->len != EOF && rc == OK; start++) {
  222.                g_status.copied = FALSE;
  223.                load_box_buff( p, ll, bc, ec, ' ' );
  224.                if (fwrite( p, sizeof( char ), len, fp ) < (unsigned)len ||
  225.                           ceh.flag == ERROR)
  226.                   rc = ERROR;
  227.                if (rc != ERROR  && fwrite( eol, sizeof( char ), eol_count, fp )
  228.                                     < eol_count || ceh.flag == ERROR)
  229.                   rc = ERROR;
  230.                ll = ll->next;
  231.                if (ll == NULL)
  232.                   rc = ERROR;
  233.             }
  234.          } else {
  235.             for (number=start; number <= end && rc == OK && ll->len != EOF;
  236.                       number++) {
  237.                g_status.copied = FALSE;
  238.                copy_line( ll );
  239.                len = g_status.line_buff_len;
  240.                if (block == STREAM) {
  241.                   if (number == start) {
  242.                      bc = bc > len ? len : bc;
  243.                      len = len - bc;
  244.  
  245.                      assert( len >= 0 );
  246.  
  247.                      memmove( p, p + bc, len );
  248.                   } else if (number == end) {
  249.                      ++ec;
  250.                      len =  ec > len ? len : ec;
  251.                   }
  252.                }
  253.  
  254.                assert( len >= 0 );
  255.                assert( len < MAX_LINE_LENGTH );
  256.  
  257.                if (fwrite( p, sizeof( char ), len, fp ) < (unsigned)len ||
  258.                        ceh.flag == ERROR)
  259.                   rc = ERROR;
  260.  
  261.                /*
  262.                 * if a Control-Z is already at EOF, don't write another one.
  263.                 */
  264.                write_eol = TRUE;
  265.                if (number == end) {
  266.                   if (file->crlf == CRLF ||  file->crlf == LF) {
  267.                      if (len > 0  &&  *(p + len - 1) == '\x1a') {
  268.                         write_eol = FALSE;
  269.                         write_z = FALSE;
  270.                      }
  271.                   }
  272.                }
  273.  
  274.                if (write_eol == TRUE  &&  rc != ERROR  &&
  275.                      fwrite( eol, sizeof( char ), eol_count, fp ) < eol_count
  276.                      || ceh.flag == ERROR)
  277.                   rc = ERROR;
  278.                ll = ll->next;
  279.                if (ll == NULL)
  280.                   rc = ERROR;
  281.             }
  282.          }
  283.          if (rc != ERROR  &&  write_z) {
  284.             if (fwrite( z, sizeof( char ), 1, fp ) < 1 || ceh.flag == ERROR)
  285.                rc = ERROR;
  286.          }
  287.          g_status.copied = FALSE;
  288.          if (ceh.flag != ERROR) {
  289.             if (fclose( fp ) != 0)
  290.                rc = ERROR;
  291.          }
  292.       }
  293.    }
  294.    return( rc );
  295. }
  296.  
  297.  
  298. /*
  299.  * Name:    hw_save
  300.  * Purpose: To save text to a file
  301.  * Date:    November 11, 1989
  302.  * Passed:  name:  name of disk file
  303.  *          file:  pointer to file structure
  304.  *          start: first character in text buffer
  305.  *          end:   last character (+1) in text buffer
  306.  *          block: type of block defined
  307.  * Returns: OK, or ERROR if anything went wrong
  308.  */
  309. int hw_save( char *name, file_infos *file, long start, long end, int block )
  310. {
  311.    return( write_file( name, OVERWRITE, file, start, end, block ) );
  312. }
  313.  
  314.  
  315. /*
  316.  * Name:    hw_append
  317.  * Purpose: To append text to a file.
  318.  * Date:    November 11, 1989
  319.  * Passed:  name:  name of disk file
  320.  *          file:  pointer to file structure
  321.  *          start: first character in text buffer
  322.  *          end:   last character (+1) in text buffer
  323.  *          block: type of defined block
  324.  * Returns: OK, or ERROR if anything went wrong
  325.  */
  326. int hw_append( char *name, file_infos *file, long start, long end, int block )
  327. {
  328.    return( write_file( name, APPEND, file, start, end, block ) );
  329. }
  330.  
  331.  
  332. /*
  333.  * Name:    load_file
  334.  * Purpose: To load a file into the array of text pointers.
  335.  * Date:    December 1, 1992
  336.  * Passed:  name:       name of disk file
  337.  *          fp:         pointer to file structure
  338.  *          file_mode:  BINARY or TEXT
  339.  *          bin_len:    if opened in BINARY mode, length of node line
  340.  * Returns: OK, or ERROR if anything went wrong
  341.  */
  342. int  load_file( char *name, file_infos *fp, int *file_mode, int bin_len )
  343. {
  344. FILE *stream;                           /* stream to read */
  345. int  rc;
  346. char buff[MAX_COLS+2];
  347. char line_buff[(MAX_COLS+2)*2];         /* buffer for char and attribute  */
  348. text_ptr l;
  349. line_list_ptr ll;
  350. line_list_ptr temp_ll;
  351. unsigned long line_count;
  352. char *e;
  353. char *residue;
  354. int  len;
  355. int  res;
  356. size_t t1, t2;
  357. int  crlf;
  358. int  prompt_line;
  359.  
  360.    /*
  361.     * initialize the counters and pointers
  362.     */
  363.    rc = OK;
  364.    len = 1;
  365.    line_count = 0;
  366.    res = 0;
  367.    residue = g_status.line_buff;
  368.    prompt_line = g_display.nlines;
  369.    fp->length  = 0;
  370.    fp->undo_count = 0;
  371.    fp->undo_top = fp->undo_bot = NULL;
  372.    fp->line_list_end = fp->line_list = NULL;
  373.    ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  374.  
  375.    if (ll != NULL) {
  376.       ll->dirty = FALSE;
  377.       ll->len   = EOF;
  378.       ll->line  = NULL;
  379.       ll->next  = ll->prev = NULL;
  380.       fp->undo_top = fp->undo_bot = ll;
  381.    }
  382.  
  383.    ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  384.  
  385.    if (ll != NULL) {
  386.       ll->dirty = FALSE;
  387.       ll->len   = EOF;
  388.       ll->line  = NULL;
  389.       ll->next  = ll->prev = NULL;
  390.       fp->line_list_end = fp->line_list = ll;
  391.    }
  392.  
  393.    if ((stream = fopen( name, "rb" )) == NULL || ceh.flag == ERROR ||
  394.          rc == ERROR) {
  395.       /*
  396.        * file not found or error loading file
  397.        */
  398.       combine_strings( buff, main7a, name, main7b );
  399.       save_screen_line( 0, prompt_line, line_buff );
  400.       set_prompt( buff, prompt_line );
  401.       getkey( );
  402.       restore_screen_line( 0, prompt_line, line_buff );
  403.       if (fp->line_list != NULL)
  404.          my_free( fp->line_list );
  405.       if (fp->undo_top != NULL)
  406.          my_free( fp->undo_top );
  407.       rc = ERROR;
  408.    } else {
  409.       if (*file_mode == BINARY) {
  410.          mode.trailing = FALSE;
  411.          crlf = BINARY;
  412.          if (bin_len < 0  ||  bin_len > READ_LENGTH)
  413.             bin_len = DEFAULT_BIN_LENGTH;
  414.          for (; rc == OK;) {
  415.             t1 = fread( g_status.line_buff, sizeof(char), bin_len, stream );
  416.             if (ferror( stream )  ||  ceh.flag == ERROR) {
  417.                combine_strings( buff, "error reading file '", name, "'" );
  418.                error( WARNING, prompt_line, buff );
  419.                rc = ERROR;
  420.             } else if (t1) {
  421.  
  422.                assert( t1 < MAX_LINE_LENGTH );
  423.  
  424.                l = (text_ptr)my_malloc( t1 * sizeof(char), &rc );
  425.                temp_ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  426.  
  427.                if (rc != ERROR) {
  428.  
  429.                   /*
  430.                    * if everything is everything, copy from io buff to
  431.                    *   dynamic mem.
  432.                    */
  433.                   if (t1 > 0)
  434.                      _fmemcpy( l, g_status.line_buff, t1 );
  435.  
  436.                   ++line_count;
  437.                   temp_ll->line = l;
  438.                   temp_ll->dirty = FALSE;
  439.                   temp_ll->len  = t1;
  440.                   insert_node( fp, ll, temp_ll );
  441.                   ll = temp_ll;
  442.                } else
  443.                   rc = show_file_2big( name, prompt_line, temp_ll, l );
  444.             } else
  445.                break;
  446.          }
  447.       } else {
  448.          crlf = LF;
  449.          for (; rc == OK;) {
  450.             t1 = fread( g_status.line_buff, sizeof(char), READ_LENGTH, stream );
  451.             if (ferror( stream )  ||  ceh.flag == ERROR) {
  452.                combine_strings( buff, "error reading file '", name, "'" );
  453.                error( WARNING, prompt_line, buff );
  454.                rc = ERROR;
  455.             } else {
  456.  
  457.                /*
  458.                 * "e" walks down io buffer 1 looking for end of lines.  "t1"
  459.                 *   keeps count of number of characters in io buffer 1.
  460.                 */
  461.                e = g_status.line_buff;
  462.                while (t1 && rc == OK) {
  463.  
  464.                   /*
  465.                    * while "t1" is not zero and "len" is less than max line length,
  466.                    *   let "e" walk down the buffer until it find <LF>.
  467.                    */
  468.                   for (; t1 && len < READ_LENGTH &&  *e != '\n'; len++, e++, t1--);
  469.  
  470.                   /*
  471.                    * if "t1" is not zero, we either found a <LF> or the line
  472.                    *   length max'd out.
  473.                    */
  474.                   if (t1  ||  len >= READ_LENGTH) {
  475.  
  476.                      if (len > 1 && *e == '\n') {
  477.                         if (len - res == 1) {
  478.                            if (*(residue + res - 1) == '\r') {
  479.                               --len;
  480.                               --res;
  481.                               crlf = CRLF;
  482.                            }
  483.                         } else {
  484.                            if (*(e - 1) == '\r') {
  485.                               --len;
  486.                               crlf = CRLF;
  487.                            }
  488.                         }
  489.                      }
  490.                      if (len > 0)
  491.                         --len;
  492.  
  493.                      assert( len >= 0 );
  494.                      assert( len < MAX_LINE_LENGTH );
  495.  
  496.                      /*
  497.                       * allocate space for relocatable array of line pointers and
  498.                       *   allocate space for the line we just read.
  499.                       */
  500.                      l = (text_ptr)my_malloc( len * sizeof(char), &rc );
  501.                      temp_ll =
  502.                        (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  503.  
  504.                      if (rc != ERROR) {
  505.  
  506.                         /*
  507.                          * if everything is everything, copy from io buff to
  508.                          *   dynamic mem.  "residue" keeps up with the beginning
  509.                          *   of line in io buffer.
  510.                          */
  511.                         if (res > 0) {
  512.  
  513.                            assert( res >= 0 );
  514.                            assert( len - res >= 0 );
  515.  
  516.                            if (res > 0)
  517.                               _fmemcpy( l, residue, res );
  518.                            if (len - res > 0)
  519.                               _fmemcpy( l + res, g_status.line_buff, len - res );
  520.                            res = 0;
  521.                         } else
  522.                            if (len > 0)
  523.                               _fmemcpy( l, residue, len );
  524.  
  525.                         ++line_count;
  526.                         temp_ll->line = l;
  527.                         temp_ll->dirty = FALSE;
  528.                         temp_ll->len  = len;
  529.                         insert_node( fp, ll, temp_ll );
  530.                         ll = temp_ll;
  531.  
  532.                         /*
  533.                          * reset io buffer pointers and counters.
  534.                          */
  535.                         len = 1;
  536.                         if (t1 == 0)
  537.                            residue = g_status.tabout_buff;
  538.                         else {
  539.                            t1--;
  540.                            residue =  t1 == 0 ? g_status.tabout_buff : ++e;
  541.                         }
  542.                      } else
  543.                         rc = show_file_2big( name, prompt_line, temp_ll, l );
  544.                   } else if (len < READ_LENGTH ) {
  545.                      if (!feof( stream ))
  546.                         res = len - 1;
  547.                   } else {
  548.                      error( WARNING, prompt_line, "FRANK: error reading file!" );
  549.                      rc = ERROR;
  550.                   }
  551.                }
  552.             }
  553.  
  554.             if (rc != OK)
  555.                break;
  556.  
  557.             /*
  558.              * we may have read all lines that end in '\n', but there may
  559.              *   be some residue after the last '\n'.  ^Z is a good example.
  560.              */
  561.             if (feof( stream )) {
  562.                if (len > 1) {
  563.                   --len;
  564.                   if (t1 == 0)
  565.                      --e;
  566.  
  567.                   assert( len >= 0 );
  568.                   assert( len < MAX_LINE_LENGTH );
  569.  
  570.                   /*
  571.                    * allocate space for relocatable array of line pointers and
  572.                    *   allocate space for the line we just read.
  573.                    */
  574.                   l = (text_ptr)my_malloc( len * sizeof(char), &rc );
  575.                   temp_ll =
  576.                        (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  577.  
  578.                   if (rc != ERROR) {
  579.  
  580.                      /*
  581.                       * if everything is everything, copy from io buff to
  582.                       *   dynamic mem.  "residue" keeps up with the beginning
  583.                       *   of line in io buffer.
  584.                       */
  585.                      if (res > 0) {
  586.  
  587.                         assert( res >= 0 );
  588.                         assert( res < MAX_LINE_LENGTH);
  589.                         assert( len - res >= 0 );
  590.                         assert( len - res < MAX_LINE_LENGTH);
  591.  
  592.                         if (res > 0 )
  593.                            _fmemcpy( l, residue, res );
  594.                         if (len - res > 0)
  595.                            _fmemcpy( l + res, g_status.line_buff, len - res );
  596.                      } else
  597.                         if (len > 0)
  598.                            _fmemcpy( l, residue, len );
  599.                      ++line_count;
  600.                      temp_ll->line = l;
  601.                      temp_ll->dirty = FALSE;
  602.                      temp_ll->len  = len;
  603.                      insert_node( fp, ll, temp_ll );
  604.                   } else
  605.                      rc = show_file_2big( name, prompt_line, temp_ll, l );
  606.                }
  607.                break;
  608.             }
  609.  
  610.             t2 = fread( g_status.tabout_buff, sizeof(char), READ_LENGTH, stream );
  611.             if (ferror( stream )  ||  ceh.flag == ERROR) {
  612.                combine_strings( buff, "error reading file '", name, "'" );
  613.                error( WARNING, prompt_line, buff );
  614.                rc = ERROR;
  615.             } else if (rc == OK) {
  616.                e = g_status.tabout_buff;
  617.                while (t2 && rc == OK) {
  618.                   for (; t2 && len < READ_LENGTH &&  *e != '\n'; len++, e++, t2--);
  619.                   if (t2  ||  len >= READ_LENGTH) {
  620.  
  621.                      if (len > 1 && *e == '\n') {
  622.                         if (len - res == 1) {
  623.                            if (*(residue + res - 1) == '\r') {
  624.                               --len;
  625.                               --res;
  626.                               crlf = CRLF;
  627.                            }
  628.                         } else {
  629.                            if (*(e - 1) == '\r') {
  630.                               --len;
  631.                               crlf = CRLF;
  632.                            }
  633.                         }
  634.                      }
  635.                      if (len > 0)
  636.                         --len;
  637.  
  638.                      assert( len >= 0 );
  639.                      assert( len < MAX_LINE_LENGTH );
  640.  
  641.                      l = (text_ptr)my_malloc( len * sizeof(char), &rc );
  642.                      temp_ll =
  643.                        (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  644.  
  645.                      if (rc != ERROR) {
  646.                         if (res > 0) {
  647.  
  648.                            assert( res >= 0 );
  649.                            assert( res < MAX_LINE_LENGTH);
  650.                            assert( len - res >= 0 );
  651.                            assert( len - res < MAX_LINE_LENGTH);
  652.  
  653.                            if (res > 0)
  654.                               _fmemcpy( l, residue, res );
  655.                            if (len - res > 0)
  656.                               _fmemcpy( l+res, g_status.tabout_buff, len - res );
  657.                            res = 0;
  658.                         } else
  659.                            if (len > 0)
  660.                               _fmemcpy( l, residue, len );
  661.  
  662.                         ++line_count;
  663.                         temp_ll->line = l;
  664.                         temp_ll->dirty = FALSE;
  665.                         temp_ll->len  = len;
  666.                         insert_node( fp, ll, temp_ll );
  667.                         ll = temp_ll;
  668.  
  669.                         len = 1;
  670.                         if (t2 == 0)
  671.                            residue = g_status.line_buff;
  672.                         else {
  673.                            t2--;
  674.                            residue =  t2 == 0 ? g_status.line_buff : ++e;
  675.                         }
  676.                      } else
  677.                         rc = show_file_2big( name, prompt_line, temp_ll, l );
  678.                   } else if (len < READ_LENGTH) {
  679.                      if (!feof( stream ))
  680.                         res = len - 1;
  681.                   } else {
  682.                      error( WARNING, prompt_line, "FRANK: error reading file!" );
  683.                      rc = ERROR;
  684.                   }
  685.                }
  686.             }
  687.  
  688.             if (rc != ERROR  &&  feof( stream )) {
  689.                if (len > 1) {
  690.                   --len;
  691.                   if (t2 == 0)
  692.                      --e;
  693.  
  694.                   assert( len >= 0 );
  695.                   assert( len < MAX_LINE_LENGTH );
  696.  
  697.                   l = (text_ptr)my_malloc( len * sizeof(char), &rc );
  698.                   temp_ll =
  699.                        (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  700.  
  701.                   if (rc != ERROR) {
  702.                      if (res > 0) {
  703.  
  704.                         assert( res >= 0 );
  705.                         assert( res < MAX_LINE_LENGTH);
  706.                         assert( len - res >= 0 );
  707.                         assert( len - res < MAX_LINE_LENGTH);
  708.  
  709.                         if (res > 0)
  710.                            _fmemcpy( l, residue, res );
  711.                         if (len - res > 0)
  712.                            _fmemcpy( l+res, g_status.tabout_buff, len - res );
  713.                      } else
  714.                         if (len > 0)
  715.                            _fmemcpy( l, residue, len );
  716.  
  717.                      ++line_count;
  718.                      temp_ll->line = l;
  719.                      temp_ll->dirty = FALSE;
  720.                      temp_ll->len  = len;
  721.                      insert_node( fp, ll, temp_ll );
  722.                   } else
  723.                      rc = show_file_2big( name, prompt_line, temp_ll, l );
  724.                }
  725.                break;
  726.             }
  727.          }
  728.          *file_mode = crlf;
  729.       }
  730.  
  731.       /*
  732.        * close the file
  733.        */
  734.       fp->length = line_count;
  735.    }
  736.    if (stream != NULL)
  737.       fclose( stream );
  738.    return( rc );
  739. }
  740.  
  741.  
  742. /*
  743.  * Name:    insert_node
  744.  * Purpose: To insert a node into a double linked list
  745.  * Date:    December 1, 1992
  746.  * Passed:  fp:  pointer to file structure that owns the double linked list
  747.  *          current: pointer to current node in double linked list
  748.  *          new:     pointer to new node to insert into double linked list
  749.  * Notes:   if the current list pointer is the last node in the list, insert
  750.  *            new code behind current node.
  751.  */
  752. void insert_node( file_infos *fp, line_list_ptr current, line_list_ptr new )
  753. {
  754.  
  755.    /*
  756.     * standard double linked list insert
  757.     */
  758.    if (current->next != NULL) {
  759.       current->next->prev = new;
  760.       new->next = current->next;
  761.       current->next = new;
  762.       new->prev = current;
  763.    /*
  764.     * if the current node is the NULL node, insert the new node behind current
  765.     */
  766.    } else {
  767.       new->next = current;
  768.       if (current->prev != NULL)
  769.          current->prev->next = new;
  770.       new->prev = current->prev;
  771.       current->prev = new;
  772.       if (new->prev == NULL)
  773.          fp->line_list = new;
  774.    }
  775. }
  776.  
  777.  
  778. /*
  779.  * Name:    show_file_2big
  780.  * Purpose: tell user we ran out of room loading file
  781.  * Date:    December 1, 1992
  782.  * Passed:  name:  name of disk file
  783.  *          line:  line to display messages
  784.  *          ll:    double linked list pointer
  785.  *          t:     text line pointer
  786.  * Returns: WARNING
  787.  * Notes:   one or both of the malloc requests overflowed the heap.  free the
  788.  *            dynamic if allocated.
  789.  */
  790. int  show_file_2big( char *name, int prompt_line, line_list_ptr ll, text_ptr t )
  791. {
  792. char buff[MAX_COLS+2];
  793.  
  794.    combine_strings( buff, main10a, name, main10b );
  795.    error( WARNING, prompt_line, buff );
  796.    if (t != NULL)
  797.       my_free( t );
  798.    if (ll != NULL)
  799.       my_free( ll );
  800.    return( WARNING );
  801. }
  802.  
  803.  
  804. /*
  805.  * Name:    backup_file
  806.  * Purpose: To make a back-up copy of current file.
  807.  * Date:    June 5, 1991
  808.  * Passed:  window:  current window pointer
  809.  */
  810. int  backup_file( WINDOW *window )
  811. {
  812. char *old_line_buff;
  813. char *old_tabout_buff;
  814. int  old_line_buff_len;
  815. int  old_tabout_buff_len;
  816. int  old_copied;
  817. int  rc;
  818. file_infos *file;
  819.  
  820.    rc = OK;
  821.    file = window->file_info;
  822.    if (file->backed_up == FALSE  &&  file->modified == TRUE) {
  823.       old_copied = g_status.copied;
  824.       old_line_buff_len = g_status.line_buff_len;
  825.       old_line_buff = calloc( MAX_LINE_LENGTH, sizeof(char) );
  826.       old_tabout_buff_len = g_status.tabout_buff_len;
  827.       old_tabout_buff = calloc( MAX_LINE_LENGTH, sizeof(char) );
  828.  
  829.       if (old_line_buff != NULL  &&  old_tabout_buff != NULL) {
  830.          memcpy( old_line_buff, g_status.line_buff, MAX_LINE_LENGTH );
  831.          memcpy( old_tabout_buff, g_status.tabout_buff, MAX_LINE_LENGTH );
  832.          if ((rc = save_backup( window )) != ERROR)
  833.             file->backed_up = TRUE;
  834.          else
  835.             rc = ERROR;
  836.          memcpy( g_status.line_buff, old_line_buff, MAX_LINE_LENGTH );
  837.          memcpy( g_status.tabout_buff, old_tabout_buff, MAX_LINE_LENGTH );
  838.          g_status.line_buff_len = old_line_buff_len;
  839.          g_status.tabout_buff_len = old_tabout_buff_len;
  840.          g_status.copied = old_copied;
  841.       } else {
  842.          error( WARNING, window->bottom_line, main4 );
  843.          rc = ERROR;
  844.       }
  845.       if (old_line_buff != NULL)
  846.          free( old_line_buff );
  847.       if (old_tabout_buff != NULL)
  848.          free( old_tabout_buff );
  849.    }
  850.    return( rc );
  851. }
  852.  
  853.  
  854. /*
  855.  * Name:    edit_file
  856.  * Purpose: To allocate space for a new file structure and set up some
  857.  *           of the relevant fields.
  858.  * Date:    June 5, 1991
  859.  * Passed:  name:  name of the file to edit
  860.  *          file_mode:  BINARY or TEXT
  861.  *          bin_length: if opened in BINARY mode, length of binary lines
  862.  * Returns: OK if file structure could be created
  863.  *          ERROR if out of memory
  864.  */
  865. int  edit_file( char *name, int file_mode, int bin_length )
  866. {
  867. int  rc;        /* return code */
  868. int  existing;
  869. int  line;
  870. int  rcol;
  871. register file_infos *file; /* file structure for file belonging to new window */
  872. file_infos *fp;
  873. long found_line;
  874. line_list_ptr ll;
  875. line_list_ptr temp_ll;
  876.  
  877.    line = g_display.nlines;
  878.    rc = OK;
  879.    /*
  880.     * allocate a file structure for the new file
  881.     */
  882.    file = (file_infos *)calloc( 1, sizeof(file_infos) );
  883.    if (file == NULL) {
  884.       error( WARNING, line, main4 );
  885.       rc = ERROR;
  886.    }
  887.    existing = FALSE;
  888.    if (rc == OK  &&  hw_fattrib( name ) == OK) {
  889.       existing = TRUE;
  890.       /*
  891.        * g_status.temp_end is set last character read in file
  892.        */
  893.  
  894.       if (g_status.command != DefineGrep  &&
  895.           g_status.command != DefineRegXGrep  &&
  896.           g_status.command != RepeatGrep)
  897.          rc = load_file( name, file, &file_mode, bin_length );
  898.       else {
  899.          if (g_status.sas_defined) {
  900.             rc = load_file( name, file, &file_mode, bin_length );
  901.             if (rc != ERROR) {
  902.                found_line = 1L;
  903.                rcol = 0;
  904.                if (g_status.sas_search_type == BOYER_MOORE)
  905.                   ll = search_forward( file->line_list, &found_line,
  906.                                        (size_t *)&rcol );
  907.                else
  908.                   ll = regx_search_forward( file->line_list, &found_line,
  909.                                             &rcol );
  910.                if (ll == NULL)
  911.                   rc = ERROR;
  912.                else {
  913.                   g_status.sas_rline = found_line;
  914.                   g_status.sas_rcol  = rcol;
  915.                   g_status.sas_ll    = ll;
  916.                }
  917.             }
  918.          } else
  919.             rc = ERROR;
  920.       }
  921.    } else {
  922.       if (ceh.flag == ERROR)
  923.          rc = ERROR;
  924.       else {
  925.          existing = FALSE;
  926.          file->length = 0l;
  927.          file->undo_top = file->undo_bot = NULL;
  928.          file->line_list_end = file->line_list = NULL;
  929.          file->undo_count = 0;
  930.          ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  931.          if (ll != NULL) {
  932.             ll->line  = NULL;
  933.             ll->next  = ll->prev = NULL;
  934.             ll->dirty = FALSE;
  935.             ll->len   = EOF;
  936.             file->undo_top = file->undo_bot = ll;
  937.          } else
  938.             rc = ERROR;
  939.  
  940.          ll = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  941.          if (ll != NULL) {
  942.             ll->line = NULL;
  943.             ll->next = ll->prev = NULL;
  944.             ll->dirty = FALSE;
  945.             ll->len   = EOF;
  946.             file->line_list_end = file->line_list = ll;
  947.          } else
  948.             rc = ERROR;
  949.          if (rc == ERROR) {
  950.             if (file->undo_top != NULL)
  951.                my_free( file->undo_top );
  952.             if (file->line_list != NULL)
  953.                my_free( file->line_list );
  954.          } else
  955.             if (file_mode == TEXT)
  956.                file_mode = CRLF;
  957.       }
  958.    }
  959.  
  960.    if (rc != ERROR) {
  961.       /*
  962.        * add file into list
  963.        */
  964.       file->prev = NULL;
  965.       file->next = NULL;
  966.       if (g_status.file_list == NULL)
  967.          g_status.file_list = file;
  968.       else {
  969.          fp = g_status.current_file;
  970.          file->prev = fp;
  971.          if (fp->next)
  972.             fp->next->prev = file;
  973.          file->next = fp->next;
  974.          fp->next = file;
  975.       }
  976.  
  977.       /*
  978.        * set up all the info we need to know about a file.
  979.        */
  980.  
  981.       assert( file_mode == CRLF  ||  file_mode == LF  ||  file_mode == BINARY );
  982.       assert( strlen( name ) < MAX_COLS );
  983.  
  984.       strcpy( file->file_name, name );
  985.       get_fattr( name, (int *)&file->file_attrib );
  986.       file->block_type  = NOTMARKED;
  987.       file->block_br    = file->block_er = 0l;
  988.       file->block_bc    = file->block_ec = 0;
  989.       file->ref_count   = 0;
  990.       file->modified    = FALSE;
  991.       file->backed_up   = FALSE;
  992.       file->new_file    = !existing;
  993.       file->next_letter = 'a';
  994.       file->file_no     = ++g_status.file_count;
  995.       file->crlf        = file_mode;
  996.       g_status.current_file = file;
  997.       make_backup_fname( file );
  998.    } else if (file != NULL) {
  999.       /*
  1000.        * free the line pointers and linked list of line pointers.
  1001.        */
  1002.       ll = file->undo_top;
  1003.       while (ll != NULL) {
  1004.          temp_ll = ll->next;
  1005.          if (ll->line != NULL)
  1006.             my_free( ll->line );
  1007.          my_free( ll );
  1008.          ll = temp_ll;
  1009.       }
  1010.  
  1011.       ll = file->line_list;
  1012.       while (ll != NULL) {
  1013.          temp_ll = ll->next;
  1014.          if (ll->line != NULL)
  1015.             my_free( ll->line );
  1016.          my_free( ll );
  1017.          ll = temp_ll;
  1018.       }
  1019.  
  1020. #if defined( __MSC__ )
  1021.       _fheapmin( );
  1022. #endif
  1023.  
  1024.       free( file );
  1025.    }
  1026.    return( rc );
  1027. }
  1028.  
  1029.  
  1030. /*
  1031.  * Name:    edit_another_file
  1032.  * Purpose: Bring in another file to editor.
  1033.  * Date:    June 5, 1991
  1034.  * Passed:  window:  pointer to current window
  1035.  * Notes:   New window replaces old window.  Old window becomes invisible.
  1036.  */
  1037. int  edit_another_file( WINDOW *window )
  1038. {
  1039. char fname[MAX_COLS];           /* new name for file */
  1040. char spdrive[_MAX_DRIVE];       /* splitpath drive buff */
  1041. char spdir[_MAX_DIR];           /* splitpath dir buff */
  1042. char spname[_MAX_FNAME];        /* splitpath fname buff */
  1043. char spext[_MAX_EXT];           /* splitpath ext buff */
  1044. register WINDOW *win;           /* put window pointer in a register */
  1045. int  rc;
  1046. int  file_mode;
  1047. int  bin_length;
  1048.  
  1049.    win = window;
  1050.    entab_linebuff( );
  1051.    if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1052.       return( ERROR );
  1053.    /*
  1054.     * read in name, no default
  1055.     */
  1056.    fname[0] = '\0';
  1057.    /*
  1058.     * file name to edit
  1059.     */
  1060.    if ((rc = get_name( ed15, win->bottom_line, fname,
  1061.                  g_display.message_color )) == OK  &&  *fname != '\0') {
  1062.       file_mode = TEXT;
  1063.       bin_length = 0;
  1064.  
  1065.       assert( strlen( fname ) <= MAX_COLS );
  1066.  
  1067.       _splitpath( fname, spdrive, spdir, spname, spext );
  1068.       if (stricmp( spext, ".exe" ) == 0  ||  stricmp( spext, ".com" ) == 0) {
  1069.          file_mode = BINARY;
  1070.          bin_length = g_status.file_chunk;
  1071.       }
  1072.       rc = attempt_edit_display( fname, LOCAL, file_mode, bin_length );
  1073.       if (rc == OK)
  1074.          show_avail_mem( );
  1075.    }
  1076.    return( rc );
  1077. }
  1078.  
  1079.  
  1080. /*
  1081.  * Name:    edit_next_file
  1082.  * Purpose: edit next file on command line.
  1083.  * Date:    January 6, 1992
  1084.  * Passed:  window:  pointer to current window
  1085.  * Notes:   New window replaces old window.  Old window becomes invisible.
  1086.  */
  1087. int  edit_next_file( WINDOW *window )
  1088. {
  1089. char name[MAX_COLS];            /* new name for file */
  1090. char spdrive[_MAX_DRIVE];       /* splitpath drive buff */
  1091. char spdir[_MAX_DIR];           /* splitpath dir buff */
  1092. char spname[_MAX_FNAME];        /* splitpath fname buff */
  1093. char spext[_MAX_EXT];           /* splitpath ext buff */
  1094. int  file_mode;
  1095. int  bin_length;
  1096. int  i;
  1097. int  update_type;
  1098. register int rc = ERROR;
  1099. register WINDOW *win;           /* put window pointer in a register */
  1100.  
  1101.    win = window;
  1102.    update_type = win == NULL ? GLOBAL : LOCAL;
  1103.    if (g_status.arg < g_status.argc) {
  1104.       if (win != NULL) {
  1105.          entab_linebuff( );
  1106.          if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1107.             return( ERROR );
  1108.       }
  1109.  
  1110.       /*
  1111.        * while we haven't found a valid file, search thru the command
  1112.        * line path.
  1113.        * we may have an invalid file name when we finish matching all
  1114.        * files according to a pattern.  then, we need to go to the next
  1115.        * command line argument if it exists.
  1116.        */
  1117.       while (rc == ERROR && g_status.arg < g_status.argc) {
  1118.  
  1119.          /*
  1120.           * if we haven't starting searching for a file, check to see if
  1121.           * the file is a valid file name.  if no file is found, then let's
  1122.           * see if we can find according to a search pattern.
  1123.           */
  1124.          if (g_status.found_first == FALSE) {
  1125.  
  1126.             assert( strlen( g_status.argv[g_status.arg] ) < MAX_COLS );
  1127.  
  1128.             strcpy( name, g_status.argv[g_status.arg] );
  1129.             rc = get_fattr( name, &i );
  1130.  
  1131.             /*
  1132.              * a new or blank file generates a return code of 2.
  1133.              * a pattern with wild cards generates a return code of 3.
  1134.              */
  1135.             if (rc == OK || rc == 2) {
  1136.                ++g_status.arg;
  1137.                rc = OK;
  1138.  
  1139.             /*
  1140.              * if we get this far, we got an invalid path name.
  1141.              *  let's try to find a matching file name using pattern.
  1142.              */
  1143.             } else if (rc != ERROR) {
  1144.                rc = my_findfirst( &g_status.dta, name, NORMAL | READ_ONLY |
  1145.                                HIDDEN | SYSTEM | ARCHIVE );
  1146.  
  1147.                /*
  1148.                 * if we found a file using wildcard characters,
  1149.                 * set the g_status.found_first flag to true so we can
  1150.                 * find the next matching file.  we need to save the
  1151.                 * pathname stem so we know which directory we are working in.
  1152.                 */
  1153.                if (rc == OK) {
  1154.                   g_status.found_first = TRUE;
  1155.                   i = strlen( name ) - 1;
  1156.                   while (i >= 0) {
  1157.                      if (name[i] == ':' || name[i] == '\\')
  1158.                         break;
  1159.                      --i;
  1160.                   }
  1161.                   name[++i] = '\0';
  1162.  
  1163.                   assert( strlen( name ) < MAX_COLS );
  1164.  
  1165.                   strcpy( g_status.path, name );
  1166.                   strcpy( name, g_status.path );
  1167.                   strcat( name, g_status.dta.name );
  1168.                } else {
  1169.                   ++g_status.arg;
  1170.                   if (win != NULL)
  1171.                      /*
  1172.                       * invalid path or file name
  1173.                       */
  1174.                      error( WARNING, win->bottom_line, win8 );
  1175.                }
  1176.             } else if (rc == ERROR)
  1177.                ++g_status.arg;
  1178.          } else {
  1179.  
  1180.             /*
  1181.              * we already found one file with wild card characters,
  1182.              * find the next matching file.
  1183.              */
  1184.             rc = my_findnext( &g_status.dta );
  1185.             if (rc == OK) {
  1186.  
  1187.                assert( strlen( g_status.path ) + strlen( g_status.dta.name )
  1188.                            < MAX_COLS );
  1189.  
  1190.                strcpy( name, g_status.path );
  1191.                strcat( name, g_status.dta.name );
  1192.             } else {
  1193.                g_status.found_first = FALSE;
  1194.                ++g_status.arg;
  1195.             }
  1196.          }
  1197.  
  1198.          /*
  1199.           * if everything is everything so far, set up the file
  1200.           * and window structures and bring the file into the editor.
  1201.           */
  1202.          if (rc == OK) {
  1203.             file_mode = g_status.file_mode;
  1204.             bin_length = g_status.file_chunk;
  1205.  
  1206.             assert( strlen( name ) <= MAX_COLS );
  1207.  
  1208.             _splitpath( name, spdrive, spdir, spname, spext );
  1209.             if (stricmp( spext, ".exe" ) == 0 || stricmp( spext, ".com" ) == 0)
  1210.                file_mode = BINARY;
  1211.             rc = attempt_edit_display( name, update_type, file_mode, bin_length );
  1212.             if (rc == OK)
  1213.                show_avail_mem( );
  1214.          }
  1215.  
  1216.          /*
  1217.           * either there are no more matching files or we had an
  1218.           * invalid file name, set rc to ERROR and let's look at the
  1219.           * next file name or pattern on the command line.
  1220.           */
  1221.          else
  1222.             rc = ERROR;
  1223.       }
  1224.    }
  1225.    if (rc == ERROR  &&  g_status.arg >= g_status.argc  &&  win != NULL)
  1226.       /*
  1227.        * no more files to load
  1228.        */
  1229.       error( WARNING, win->bottom_line, win9 );
  1230.    return( rc );
  1231. }
  1232.  
  1233.  
  1234. /*
  1235.  * Name:    search_and_seize
  1236.  * Purpose: search files for a pattern
  1237.  * Date:    October 31, 1992
  1238.  * Passed:  window:  pointer to current window
  1239.  * Notes:   New window replaces old window.  Old window becomes invisible.
  1240.  */
  1241. int  search_and_seize( WINDOW *window )
  1242. {
  1243. char name[MAX_COLS];            /* new name for file */
  1244. char searching[MAX_COLS];       /* buffer for displaying file name */
  1245. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1246. char spdrive[_MAX_DRIVE];       /* splitpath drive buff */
  1247. char spdir[_MAX_DIR];           /* splitpath dir buff */
  1248. char spname[_MAX_FNAME];        /* splitpath fname buff */
  1249. char spext[_MAX_EXT];           /* splitpath ext buff */
  1250. int  file_mode;
  1251. int  bin_length;
  1252. int  i;
  1253. int  update_type;
  1254. char *tokens;
  1255. register int rc = ERROR;
  1256. register WINDOW *win;           /* put window pointer in a register */
  1257. int  bottom_line;
  1258.  
  1259.    win = window;
  1260.    update_type = win == NULL ? GLOBAL : LOCAL;
  1261.    if (update_type == LOCAL) {
  1262.       if (!g_status.sas_defined ||  g_status.command == DefineGrep ||
  1263.                               g_status.command == DefineRegXGrep) {
  1264.  
  1265.          /*
  1266.           * prompt for the search pattern and the seize path.
  1267.           *   initialize all this stuff.
  1268.           */
  1269.          if (g_status.command == DefineGrep)
  1270.             g_status.sas_search_type = BOYER_MOORE;
  1271.          else
  1272.             g_status.sas_search_type = REG_EXPRESSION;
  1273.  
  1274.          if (g_status.sas_search_type == BOYER_MOORE) {
  1275.             *sas_bm.pattern = '\0';
  1276.             if (get_name( win16a, win->bottom_line, (char *)sas_bm.pattern,
  1277.                              g_display.message_color ) == ERROR)
  1278.                return( ERROR );
  1279.             if (*sas_bm.pattern == '\0')
  1280.                return( ERROR );
  1281.          } else {
  1282.             *sas_regx.pattern = '\0';
  1283.             if (get_name( win16b, win->bottom_line, (char *)sas_regx.pattern,
  1284.                              g_display.message_color ) == ERROR)
  1285.                return( ERROR );
  1286.             if (*sas_regx.pattern == '\0')
  1287.                return( ERROR );
  1288.             else
  1289.                strcpy( (char *)regx.pattern, (char *)sas_regx.pattern );
  1290.          }
  1291.          *g_status.sas_tokens = '\0';
  1292.          if (get_name( win17, win->bottom_line, g_status.sas_tokens,
  1293.                           g_display.message_color ) == ERROR)
  1294.             return( ERROR );
  1295.          i = 0;
  1296.          tokens = strtok( g_status.sas_tokens, SAS_DELIMITERS );
  1297.          while (tokens != NULL) {
  1298.             g_status.sas_arg_pointers[i++] = tokens;
  1299.             tokens = strtok( NULL, SAS_DELIMITERS );
  1300.          }
  1301.          if (i == 0)
  1302.             return( ERROR );
  1303.          g_status.sas_arg_pointers[i] = NULL;
  1304.          g_status.sas_argc = i;
  1305.          g_status.sas_arg = 0;
  1306.          g_status.sas_argv = g_status.sas_arg_pointers;
  1307.          g_status.sas_found_first = FALSE;
  1308.          if (g_status.command == DefineGrep) {
  1309.             g_status.sas_defined = TRUE;
  1310.             bm.search_defined = sas_bm.search_defined = OK;
  1311.             build_boyer_array( );
  1312.          } else {
  1313.             i = build_nfa( );
  1314.             if (i == OK) {
  1315.                g_status.sas_defined = TRUE;
  1316.                regx.search_defined = sas_regx.search_defined = OK;
  1317.             } else
  1318.                g_status.sas_defined = FALSE;
  1319.          }
  1320.       }
  1321.       bottom_line = win->bottom_line;
  1322.    } else
  1323.       bottom_line = g_display.nlines;
  1324.    if (g_status.sas_defined && g_status.sas_arg < g_status.sas_argc) {
  1325.       if (win != NULL) {
  1326.          entab_linebuff( );
  1327.          un_copy_line( win->ll, win, TRUE );
  1328.       }
  1329.  
  1330.       /*
  1331.        * while we haven't found a valid file, search thru the command
  1332.        * line path.
  1333.        * we may have an invalid file name when we finish matching all
  1334.        * files according to a pattern.  then, we need to go to the next
  1335.        * command line argument if it exists.
  1336.        */
  1337.       while (rc == ERROR && g_status.sas_arg < g_status.sas_argc) {
  1338.  
  1339.          /*
  1340.           * if we haven't starting searching for a file, check to see if
  1341.           * the file is a valid file name.  if no file is found, then let's
  1342.           * see if we can find according to a search pattern.
  1343.           */
  1344.          if (g_status.sas_found_first == FALSE) {
  1345.  
  1346.             assert( strlen( g_status.sas_argv[g_status.sas_arg] ) < MAX_COLS );
  1347.  
  1348.             strcpy( name, g_status.sas_argv[g_status.sas_arg] );
  1349.             rc = get_fattr( name, &i );
  1350.  
  1351.             /*
  1352.              * a new or blank file generates a return code of 2.
  1353.              * a pattern with wild cards generates a return code of 3.
  1354.              */
  1355.             if (rc == OK || rc == 2) {
  1356.                ++g_status.sas_arg;
  1357.                rc = OK;
  1358.  
  1359.             /*
  1360.              * if we get this far, we got an invalid path name.
  1361.              *  let's try to find a matching file name using pattern.
  1362.              */
  1363.             } else if (rc != ERROR) {
  1364.                rc = my_findfirst( &g_status.sas_dta, name, NORMAL | READ_ONLY |
  1365.                                HIDDEN | SYSTEM | ARCHIVE );
  1366.  
  1367.                /*
  1368.                 * if we found a file using wildcard characters,
  1369.                 * set the g_status.sas_found_first flag to true so we can
  1370.                 * find the next matching file.  we need to save the
  1371.                 * pathname stem so we know which directory we are working in.
  1372.                 */
  1373.                if (rc == OK) {
  1374.                   g_status.sas_found_first = TRUE;
  1375.                   i = strlen( name ) - 1;
  1376.                   while (i >= 0) {
  1377.                      if (name[i] == ':' || name[i] == '\\')
  1378.                         break;
  1379.                      --i;
  1380.                   }
  1381.                   name[++i] = '\0';
  1382.  
  1383.                   assert( strlen( name ) + strlen( g_status.sas_dta.name )
  1384.                                    < MAX_COLS );
  1385.  
  1386.                   strcpy( g_status.sas_path, name );
  1387.                   strcpy( name, g_status.sas_path );
  1388.                   strcat( name, g_status.sas_dta.name );
  1389.                } else {
  1390.                   ++g_status.sas_arg;
  1391.                   if (win != NULL)
  1392.                      /*
  1393.                       * invalid path or file name
  1394.                       */
  1395.                      error( WARNING, win->bottom_line, win8 );
  1396.                }
  1397.             } else if (rc == ERROR)
  1398.                ++g_status.sas_arg;
  1399.          } else {
  1400.  
  1401.             /*
  1402.              * we already found one file with wild card characters,
  1403.              * find the next matching file.
  1404.              */
  1405.             rc = my_findnext( &g_status.sas_dta );
  1406.             if (rc == OK) {
  1407.  
  1408.                assert( strlen( g_status.sas_path ) +
  1409.                        strlen( g_status.sas_dta.name ) < MAX_COLS );
  1410.  
  1411.                strcpy( name, g_status.sas_path );
  1412.                strcat( name, g_status.sas_dta.name );
  1413.             } else {
  1414.                g_status.sas_found_first = FALSE;
  1415.                ++g_status.sas_arg;
  1416.             }
  1417.          }
  1418.  
  1419.          /*
  1420.           * if everything is everything so far, set up the file
  1421.           * and window structures and bring the file into the editor.
  1422.           */
  1423.          if (rc == OK) {
  1424.  
  1425.             assert( strlen( win19 ) + strlen( name ) < MAX_COLS );
  1426.  
  1427.             strcpy( searching, win19 );
  1428.             strcat( searching, name );
  1429.             save_screen_line( 0, bottom_line, line_buff );
  1430.             set_prompt( searching, bottom_line );
  1431.             file_mode = TEXT;
  1432.             bin_length = 0;
  1433.  
  1434.             assert( strlen( name ) <= MAX_COLS );
  1435.  
  1436.             _splitpath( name, spdrive, spdir, spname, spext );
  1437.             if (stricmp( spext, ".exe" ) == 0 || stricmp( spext, ".com" ) == 0){
  1438.                file_mode = BINARY;
  1439.                bin_length = g_status.file_chunk;
  1440.             }
  1441.             rc = attempt_edit_display( name, update_type, file_mode, bin_length );
  1442.             if (rc == OK)
  1443.                show_avail_mem( );
  1444.             restore_screen_line( 0, bottom_line, line_buff );
  1445.  
  1446.             if (rc == OK) {
  1447.                win = g_status.current_window;
  1448.                bin_offset_adjust( win, g_status.sas_rline );
  1449.                find_adjust( win, g_status.sas_ll, g_status.sas_rline,
  1450.                             g_status.sas_rcol );
  1451.                make_ruler( win );
  1452.                show_ruler( win );
  1453.                show_ruler_pointer( win );
  1454.                show_window_header( win );
  1455.                if (win->vertical)
  1456.                   show_vertical_separator( win );
  1457.                win->file_info->dirty = LOCAL;
  1458.             }
  1459.          }
  1460.  
  1461.          /*
  1462.           * either there are no more matching files or we had an
  1463.           * invalid file name, set rc to ERROR and let's look at the
  1464.           * next file name or pattern on the command line.
  1465.           */
  1466.          else
  1467.             rc = ERROR;
  1468.       }
  1469.    }
  1470.    if (rc == ERROR &&  g_status.sas_arg >= g_status.sas_argc  && win != NULL)
  1471.       /*
  1472.        * no more files to load
  1473.        */
  1474.       error( WARNING, win->bottom_line, win9 );
  1475.    return( rc );
  1476. }
  1477.  
  1478.  
  1479. /*
  1480.  * Name:    attempt_edit_display
  1481.  * Purpose: try to load then display a file
  1482.  * Date:    June 5, 1991
  1483.  * Passed:  fname:       file name to load
  1484.  *          update_type: update current window or entire screen
  1485.  *          file_mode:   BINARY or TEXT
  1486.  *          bin_len:     if opened in BINARY mode, length of binary lines
  1487.  * Notes:   When we first start the editor, we need to update the entire
  1488.  *          screen.  When we load in a new file, we only need to update
  1489.  *          the current window.
  1490.  */
  1491. int  attempt_edit_display( char *fname, int update_type, int file_mode,
  1492.                            int bin_len )
  1493. {
  1494. register int rc;
  1495. WINDOW *win;
  1496.  
  1497.    rc = edit_file( fname, file_mode, bin_len );
  1498.    if (rc != ERROR) {
  1499.       rc = initialize_window( );
  1500.       if (rc != ERROR) {
  1501.          win = g_status.current_window;
  1502.          if (update_type == LOCAL) {
  1503.             if (g_status.command != DefineGrep  &&
  1504.                         g_status.command != DefineRegXGrep  &&
  1505.                         g_status.command != RepeatGrep)
  1506.                redraw_current_window( win );
  1507.             show_file_count( g_status.file_count );
  1508.             show_window_count( g_status.window_count );
  1509.             show_avail_mem( );
  1510.          } else if (update_type == GLOBAL)
  1511.             redraw_screen( win );
  1512.          if (win->file_info->new_file) {
  1513.             g_status.command = AddLine;
  1514.             insert_newline( win );
  1515.             win->file_info->modified = FALSE;
  1516.          }
  1517.       }
  1518.    }
  1519.    return( rc );
  1520. }
  1521.  
  1522.  
  1523. /*
  1524.  * Name:    make_backup_fname
  1525.  * Purpose: add .bak to file name
  1526.  * Date:    January 6, 1992
  1527.  * Passed:  file: information allowing access to the current file
  1528.  */
  1529. void make_backup_fname( file_infos *file )
  1530. {
  1531. char name[MAX_COLS];            /* new name for file */
  1532. char *p;
  1533. int  i;
  1534. int  len;
  1535.  
  1536.    /*
  1537.     * if this is a new file then don't create a backup - can't backup
  1538.     *   a nonexisting file.
  1539.     */
  1540.    if (file->new_file)
  1541.       file->backed_up = TRUE;
  1542.  
  1543.    /*
  1544.     * find the file name extension if it exists
  1545.     */
  1546.    else {
  1547.       assert( strlen( file->file_name ) < MAX_COLS );
  1548.       strcpy( name, file->file_name );
  1549.       len = strlen( name );
  1550.       for (i=len,p=name+len; i>=0; i--) {
  1551.  
  1552.          /*
  1553.           * we found the '.' extension character.  get out
  1554.           */
  1555.          if (*p == '.')
  1556.             break;
  1557.  
  1558.          /*
  1559.           * we found the drive or directory character.  no extension so
  1560.           *  set the pointer to the end of file name string.
  1561.           */
  1562.          else if (*p == '\\' || *p == ':') {
  1563.             p = name + len;
  1564.             break;
  1565.  
  1566.          /*
  1567.           * we're at the beginning of the string - no '.', drive, or directory
  1568.           *  char was found.  set the pointer to the end of file name string.
  1569.           */
  1570.          } else if (i == 0) {
  1571.             p = name + len;
  1572.             break;
  1573.          }
  1574.          --p;
  1575.       }
  1576.       assert( strlen( name ) < MAX_COLS );
  1577.       strcpy( p, ".bak" );
  1578.       strcpy( file->backup_fname, name );
  1579.    }
  1580. }
  1581.  
  1582.  
  1583. /*
  1584.  * Name:    file_file
  1585.  * Purpose: To file the current file to disk.
  1586.  * Date:    September 17, 1991
  1587.  * Passed:  window:  pointer to current window
  1588.  */
  1589. int  file_file( WINDOW *window )
  1590. {
  1591.    if (save_file( window ) == OK)
  1592.       finish( window );
  1593.    return( OK );
  1594. }
  1595.  
  1596.  
  1597. /*
  1598.  * Name:    save_file
  1599.  * Purpose: To save the current file to disk.
  1600.  * Date:    June 5, 1991
  1601.  * Passed:  window:  pointer to current window
  1602.  * Notes:   If anything goes wrong, then the modified flag is set.
  1603.  *          If the file is saved successfully, then modified flag is
  1604.  *           cleared.
  1605.  */
  1606. int  save_file( WINDOW *window )
  1607. {
  1608. char name[MAX_COLS]; /* name of file to be saved */
  1609. register file_infos *file;
  1610. int  rc;
  1611. line_list_ptr temp_ll;
  1612.  
  1613.    entab_linebuff( );
  1614.    if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  1615.       return( ERROR );
  1616.    file = window->file_info;
  1617.    if (file->modified == FALSE)
  1618.       return( OK );
  1619.    /*
  1620.     * set up file name
  1621.     */
  1622.    assert( strlen( file->file_name ) < MAX_COLS );
  1623.    strcpy( name, file->file_name );
  1624.  
  1625.    /*
  1626.     * see if there was a file name - if not, then make the user
  1627.     *  supply one.
  1628.     */
  1629.    if (strlen( name ) == 0)
  1630.       rc = save_as_file( window );
  1631.    else {
  1632.       /*
  1633.        * save the file
  1634.        */
  1635.       rc = write_to_disk( window, name );
  1636.       if (rc != ERROR) {
  1637.          file->modified = FALSE;
  1638.          file->new_file = FALSE;
  1639.       }
  1640.    }
  1641.  
  1642.    /*
  1643.     * clear the dirty flags
  1644.     */
  1645.    if (rc == OK) {
  1646.       temp_ll = window->file_info->line_list;
  1647.       for (; temp_ll->len != EOF; temp_ll=temp_ll->next)
  1648.          temp_ll->dirty = FALSE;
  1649.       window->file_info->dirty = GLOBAL;
  1650.    }
  1651.    return( rc );
  1652. }
  1653.  
  1654.  
  1655. /*
  1656.  * Name:    save_backup
  1657.  * Purpose: To save a backup copy of the current file to disk.
  1658.  * Date:    June 5, 1991
  1659.  * Passed:  window:  pointer to current window
  1660.  */
  1661. int  save_backup( WINDOW *window )
  1662. {
  1663.    /*
  1664.     * set up file name
  1665.     */
  1666.    return( write_to_disk( window, window->file_info->backup_fname ) );
  1667. }
  1668.  
  1669.  
  1670. /*
  1671.  * Name:    write_to_disk
  1672.  * Purpose: To write file from memory to disk
  1673.  * Date:    June 5, 1991
  1674.  * Passed:  window:  pointer to current window
  1675.  *          fname:   file name to save on disk
  1676.  */
  1677. int  write_to_disk( WINDOW *window, char *fname )
  1678. {
  1679. char name[MAX_COLS]; /* name of file to be saved */
  1680. char status_line[MAX_COLS+2]; /* status line at top of window */
  1681. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1682. register file_infos *file;
  1683. int  rc;
  1684. int  prompt_line;
  1685. int  fattr;
  1686.  
  1687.    file = window->file_info;
  1688.    prompt_line = window->bottom_line;
  1689.  
  1690.    /*
  1691.     * set up file name
  1692.     */
  1693.    assert( strlen( fname ) < MAX_COLS );
  1694.    strcpy( name, fname );
  1695.    save_screen_line( 0, prompt_line, line_buff );
  1696.    eol_clear( 0, prompt_line, g_display.message_color );
  1697.  
  1698.    /*
  1699.     * saving
  1700.     */
  1701.    combine_strings( status_line, utils6, name, "'" );
  1702.    s_output( status_line, prompt_line, 0, g_display.message_color );
  1703.    if ((rc = hw_save( name, file, 1L, file->length, NOTMARKED )) == ERROR) {
  1704.       if (ceh.flag != ERROR) {
  1705.          if (get_fattr( name, &fattr ) == OK && fattr & READ_ONLY)
  1706.             /*
  1707.              * file is read only
  1708.              */
  1709.             combine_strings( status_line, utils7a, name, utils7b );
  1710.          else
  1711.             /*
  1712.              * cannot write to
  1713.              */
  1714.             combine_strings( status_line, utils8, name, "'" );
  1715.          error( WARNING, prompt_line, status_line );
  1716.       }
  1717.    }
  1718.    restore_screen_line( 0, prompt_line, line_buff );
  1719.    return( rc );
  1720. }
  1721.  
  1722.  
  1723. /*
  1724.  * Name:    save_as_file
  1725.  * Purpose: To save the current file to disk, but under a new name.
  1726.  * Date:    June 5, 1991
  1727.  * Passed:  window:  pointer to current window
  1728.  */
  1729. int  save_as_file( WINDOW *window )
  1730. {
  1731. char name[MAX_COLS];            /* new name for file */
  1732. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1733. int  prompt_line;
  1734. int  rc;
  1735. int  fattr;
  1736. register WINDOW *win;           /* put window pointer in a register */
  1737. line_list_ptr temp_ll;
  1738.  
  1739.    win = window;
  1740.    entab_linebuff( );
  1741.    if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1742.       return( ERROR );
  1743.    /*
  1744.     * read in name
  1745.     */
  1746.    prompt_line = win->bottom_line;
  1747.    save_screen_line( 0, prompt_line, line_buff );
  1748.    name[0] = '\0';
  1749.    /*
  1750.     * new file name:
  1751.     */
  1752.    if ((rc = get_name( utils9, prompt_line, name,
  1753.                        g_display.message_color )) == OK  &&  *name != '\0') {
  1754.  
  1755.        /*
  1756.         * make sure it is OK to overwrite any existing file
  1757.         */
  1758.       rc = get_fattr( name, &fattr );
  1759.       if (rc == OK) {   /* file exists */
  1760.          /*
  1761.           * overwrite existing file?
  1762.           */
  1763.          set_prompt( utils10, prompt_line );
  1764.          if (get_yn( ) != A_YES  ||  change_mode( name, prompt_line ) == ERROR)
  1765.             rc = ERROR;
  1766.       }
  1767.       if (rc != ERROR)
  1768.          rc = write_to_disk( win, name );
  1769.  
  1770.       /*
  1771.        * depending on personal taste, you may want to uncomment the next
  1772.        *  lines to clear the dirty flags.
  1773.        */
  1774. /*
  1775.  *     if (rc == OK) {
  1776.  *        temp_ll = window->file_info->line_list;
  1777.  *        for (; temp_ll->len != EOF; temp_ll=temp_ll->next)
  1778.  *           temp_ll->dirty = FALSE;
  1779.  *        window->file_info->dirty = GLOBAL;
  1780.  *     }
  1781.  */
  1782.    }
  1783.    restore_screen_line( 0, prompt_line, line_buff );
  1784.    return( rc );
  1785. }
  1786.  
  1787.  
  1788. /*
  1789.  * Name:    change_fattr
  1790.  * Purpose: To change the file attributes
  1791.  * Date:    December 31, 1991
  1792.  * Passed:  window:  pointer to current window
  1793.  */
  1794. int  change_fattr( WINDOW *window )
  1795. {
  1796. char name[MAX_COLS];            /* new name for file */
  1797. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1798. file_infos *file;
  1799. WINDOW *wp;
  1800. int  prompt_line;
  1801. register int ok;
  1802. unsigned char fattr;
  1803. char *s;
  1804. int  rc;
  1805.  
  1806.    prompt_line = window->bottom_line;
  1807.    save_screen_line( 0, prompt_line, line_buff );
  1808.    name[0] = '\0';
  1809.    /*
  1810.     * enter new file attributes
  1811.     */
  1812.    if ((ok = get_name( utils14, prompt_line, name,
  1813.                        g_display.message_color )) == OK) {
  1814.       if (*name != '\0') {
  1815.          fattr = 0;
  1816.          s = name;
  1817.  
  1818.          /*
  1819.           * yes, I know lint complains about "ok = *s++".
  1820.           */
  1821.          while (ok = *s++) {
  1822.             switch (ok) {
  1823.                case 'a' :
  1824.                case 'A' :
  1825.                   fattr |= ARCHIVE;
  1826.                   break;
  1827.                case 's' :
  1828.                case 'S' :
  1829.                   fattr |= SYSTEM;
  1830.                   break;
  1831.                case 'h' :
  1832.                case 'H' :
  1833.                   fattr |= HIDDEN;
  1834.                   break;
  1835.                case 'r' :
  1836.                case 'R' :
  1837.                   fattr |= READ_ONLY;
  1838.                   break;
  1839.                default :
  1840.                   break;
  1841.             }
  1842.          }
  1843.          file = window->file_info;
  1844.          if (set_fattr( file->file_name, fattr ))
  1845.             /*
  1846.              * new file attributes not set
  1847.              */
  1848.             error( WARNING, prompt_line, utils15 );
  1849.          else {
  1850.             file->file_attrib = fattr;
  1851.             for (wp=g_status.window_list; wp!=NULL; wp=wp->next) {
  1852.                if (wp->file_info == file && wp->visible)
  1853.                   show_window_fname( wp );
  1854.             }
  1855.          }
  1856.       }
  1857.       rc = OK;
  1858.    } else
  1859.       rc = ERROR;
  1860.    restore_screen_line( 0, prompt_line, line_buff );
  1861.    return( rc );
  1862. }
  1863.  
  1864.  
  1865. /*
  1866.  * Name:    get_fattr
  1867.  * Purpose: To get dos file attributes
  1868.  * Date:    December 26, 1991
  1869.  * Passed:  fname: ASCIIZ file name.  Null terminated file name
  1870.  *          fattr: pointer to file attributes
  1871.  * Returns: 0 if successfull, non zero if not
  1872.  * Notes:   Uses the DOS function to get file attributes.  I really didn't
  1873.  *           like the file attribute functions in the C library:  fstat() and
  1874.  *           stat() or access() and chmod().
  1875.  *           FYI, File Attributes:
  1876.  *              0x00 = Normal.  Can be read or written w/o restriction
  1877.  *              0x01 = Read-only.  Cannot be opened for write; a file with
  1878.  *                     the same name cannot be created.
  1879.  *              0x02 = Hidden.  Not found by directory search.
  1880.  *              0x04 = System.  Not found by directory search.
  1881.  *              0x08 = Volumn Label.
  1882.  *              0x10 = Directory.
  1883.  *              0x20 = Archive.  Set whenever the file is changed, or
  1884.  *                     cleared by the Backup command.
  1885.  *           Return codes:
  1886.  *              0 = No error
  1887.  *              1 = AL not 0 or 1
  1888.  *              2 = file is invalid or does not exist
  1889.  *              3 = path is invalid or does not exist
  1890.  *              5 = Access denied
  1891.  */
  1892. int  get_fattr( char far *fname, int *fattr )
  1893. {
  1894. int  rc;                /* return code */
  1895. int  attr;
  1896.  
  1897.    ASSEMBLE {
  1898.         push    ds
  1899.         mov     dx, WORD PTR fname      /* get OFFSET of filename string */
  1900.         mov     ax, WORD PTR fname+2    /* get SEGMENT of filename string */
  1901.         mov     ds, ax                  /* put SEGMENT in ds */
  1902.         mov     ax, 0x4300              /* function:  get file attributes */
  1903.         int     0x21                    /* DOS interrupt */
  1904.         pop     ds
  1905.  
  1906.         jc      an_error                /* save the error code from get attr */
  1907.         xor     ax, ax                  /* if no carry, no error */
  1908.         jmp     SHORT get_out           /* lets get out */
  1909.    }
  1910. an_error:
  1911.  
  1912.  
  1913.    ASSEMBLE {
  1914.         xor     cx, cx                  /* if error, then zero out cx - attrs */
  1915.    }
  1916. get_out:
  1917.  
  1918.    ASSEMBLE {
  1919.         mov     WORD PTR rc, ax         /* ax contains error number on error */
  1920.         mov     WORD PTR attr, cx       /* cx contains file attributes */
  1921.    }
  1922.    *fattr = attr;
  1923.    if (ceh.flag == ERROR)
  1924.       rc = ERROR;
  1925.    return( rc );
  1926. }
  1927.  
  1928.  
  1929. /*
  1930.  * Name:    set_fattr
  1931.  * Purpose: To set dos file attributes
  1932.  * Date:    December 26, 1991
  1933.  * Passed:  fname: ASCIIZ file name.  Null terminated file name
  1934.  *          fattr: file attributes
  1935.  * Returns: 0 if successfull, non zero if not
  1936.  * Notes:   Uses the DOS function to get file attributes.
  1937.  *           Return codes:
  1938.  *              0 = No error
  1939.  *              1 = AL not 0 or 1
  1940.  *              2 = file is invalid or does not exist
  1941.  *              3 = path is invalid or does not exist
  1942.  *              5 = Access denied
  1943.  */
  1944. int  set_fattr( char far *fname, int fattr )
  1945. {
  1946. int  rc;                /* return code */
  1947.  
  1948.    ASSEMBLE {
  1949.         push    ds
  1950.         mov     dx, WORD PTR fname      /* get OFFSET of filename string */
  1951.         mov     ax, WORD PTR fname+2    /* get SEGMENT of filename string */
  1952.         mov     ds, ax                  /* put SEGMENT in ds */
  1953.         mov     cx, WORD PTR fattr      /* cx contains file attributes */
  1954.         mov     ax, 0x4301              /* function:  get file attributes */
  1955.         int     0x21                    /* DOS interrupt */
  1956.         pop     ds
  1957.  
  1958.         jc      get_out                 /* save the error code from get attr */
  1959.         xor     ax, ax                  /* if no carry, no error */
  1960.    }
  1961. get_out:
  1962.  
  1963.    ASSEMBLE {
  1964.         mov     WORD PTR rc, ax         /* ax contains error number on error */
  1965.    }
  1966.    if (ceh.flag == ERROR)
  1967.       rc = ERROR;
  1968.    return( rc );
  1969. }
  1970.  
  1971.  
  1972. /*
  1973.  * Name:    get_current_directory
  1974.  * Purpose: get current directory
  1975.  * Date:    February 13, 1992
  1976.  * Passed:  path:  pointer to buffer to store path
  1977.  *          drive: drive to get current directory
  1978.  * Notes:   use simple DOS interrupt
  1979.  */
  1980. int  get_current_directory( char far *path, int drive )
  1981. {
  1982. int  rc;
  1983.  
  1984.    ASSEMBLE {
  1985.         push    si                      /* save register vars if any */
  1986.         push    ds                      /* save ds */
  1987.  
  1988.         mov     dx, WORD PTR drive      /* dl = drive, 0 = default, 1 = a, etc.. */
  1989.         mov     si, WORD PTR path       /* get OFFSET of path */
  1990.         mov     ax, WORD PTR path+2     /* get SEGMENT of path */
  1991.         mov     ds, ax                  /* put it in ds */
  1992.         mov     ah, 0x47                /* function 0x47 == get current dir */
  1993.         int     0x21                    /* standard DOS interrupt */
  1994.         xor     ax, ax                  /* zero out ax, return OK if no error */
  1995.         jnc     no_error                /* if carry set, then an error */
  1996.         mov     ax, ERROR               /* return -1 if error */
  1997.    }
  1998. no_error:
  1999.  
  2000.    ASSEMBLE {
  2001.         pop     ds                      /* get back ds */
  2002.         pop     si                      /* get back si */
  2003.         mov     WORD PTR rc, ax         /* save return code */
  2004.    }
  2005.    if (ceh.flag == ERROR)
  2006.       rc = ERROR;
  2007.    return( rc );
  2008. }
  2009.  
  2010.  
  2011. /*
  2012.  * Name:    set_current_directory
  2013.  * Purpose: set current directory
  2014.  * Date:    February 13, 1992
  2015.  * Passed:  new_path: directory path, which may include drive letter
  2016.  * Notes:   use simple DOS interrupt
  2017.  */
  2018. int  set_current_directory( char far *new_path )
  2019. {
  2020. int  rc;
  2021.  
  2022.    ASSEMBLE {
  2023.         push    ds                      /* save ds */
  2024.  
  2025.         mov     dx, WORD PTR new_path   /* get OFFSET of new_path */
  2026.         mov     ax, WORD PTR new_path+2 /* get SEGMENT of new_path */
  2027.         mov     ds, ax                  /* put it in ds */
  2028.         mov     ah, 0x3b                /* function 0x3b == set current dir */
  2029.         int     0x21                    /* standard DOS interrupt */
  2030.         xor     ax, ax                  /* zero out ax, return OK if no error */
  2031.         jnc     no_error                /* if carry set, then an error */
  2032.         mov     ax, ERROR               /* return -1 if error */
  2033.    }
  2034. no_error:
  2035.  
  2036.    ASSEMBLE {
  2037.         pop     ds                      /* get back ds */
  2038.         mov     WORD PTR rc, ax         /* save return code */
  2039.    }
  2040.    if (ceh.flag == ERROR)
  2041.       rc = ERROR;
  2042.    return( rc );
  2043. }
  2044.